package qos.estimator.statistics;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.Semaphore;

import qos.estimator.exception.CollectorException;

public class MemoryStatisticsCollector implements IStatisticsCollector {
	
	private Double memory = 0d;
	private Double minorFaultsPerSecond = 0d;
	private Double majorFaultsPerSecond = 0d;
	private Double virtualSize = 0d;
	private Double residentSetSize = 0d;
	private Map<String, Double> memoryUtilization = new HashMap<String, Double>();
	private Semaphore semaphore = new Semaphore(1);
	private boolean forceStop = false;
	
	public MemoryStatisticsCollector() {
		new Thread(new Runnable(){

			public void run() {
				try {
					
					Process process = Runtime.getRuntime().exec("pidstat -p ALL -rh 5");
					java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()));
					String line = null;  
					while ((line = in.readLine()) != null) {
						if(forceStop)
							return;
						StringTokenizer st = new StringTokenizer(line, " ");
						try{
							String token = st.nextToken();
							Long.parseLong(token);
						}catch (Exception e) {
							semaphore.acquire();
							updateMemoryUtilizationMap();
							resetCounters();
							continue;
						} finally{
							semaphore.release();
						}
						
						//ignore first two tokens (time and #pid)
						st.nextToken();
						increaseCounters(st);
					}
				} catch (IOException e) {
					e.printStackTrace();
					memory = -1d;
				} catch (InterruptedException e) {
					e.printStackTrace();
					memory = -1d;
				} finally{
					semaphore.release();
				}
			}

			private void updateMemoryUtilizationMap() {
				if(memory == 0)
					return;
				memoryUtilization.put("memory", memory);
				memoryUtilization.put("minorFaultsPerSecond", minorFaultsPerSecond);
				memoryUtilization.put("majorFaultsPerSecond", majorFaultsPerSecond);
				memoryUtilization.put("virtualSize", virtualSize);
				memoryUtilization.put("residentSetSize", residentSetSize);
			}

			private void increaseCounters(StringTokenizer st) {
				minorFaultsPerSecond += Double.parseDouble(st.nextToken().replace(",", "."));
				majorFaultsPerSecond += Double.parseDouble(st.nextToken().replace(",", "."));
				virtualSize += Double.parseDouble(st.nextToken().replace(",", "."));	
				residentSetSize += Double.parseDouble(st.nextToken().replace(",", "."));
				memory += Double.parseDouble(st.nextToken().replace(",", "."));
			}

			private void resetCounters() {
				
				minorFaultsPerSecond = 0d;
				majorFaultsPerSecond = 0d;
				virtualSize = 0d;	
				residentSetSize = 0d;
				memory = 0d;
			}
			
			
		}).start();
	}
	

	public void forceStop(){
		forceStop = true;
	}
	
	public Map<String, Double> next() throws CollectorException {
		try {
			semaphore.acquire();
			//FIXME Should return all variables (perhaps the whole Map?)
			Map<String, Double> nonVolatileMap = new HashMap<String, Double>(memoryUtilization);
			return nonVolatileMap;
		} catch (InterruptedException e) {
			throw new CollectorException(e);
		} finally{
			semaphore.release();
		}
		
		
	}

	public static void main(String[] args) throws Exception {
		MemoryStatisticsCollector test = new MemoryStatisticsCollector();
		for(int i = 0; i <10; i++){
			Thread.sleep(5000);
			System.out.println("Utilizacao da memória no passo "+(i+1)+": "+((Map)test.next()).get("memory"));
		}
		test.forceStop();
	}
	
}
